package com.md.util;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.experimental.UtilityClass;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author 刘骄阳
 * 2022-12-06 23:31
 */
@UtilityClass
public class CollectionUtil {
  /**
   * 根据map的key排序
   */
  public static <K extends Comparable<? super K>, V> Map<K, V> orderByKey(final Map<K, V> map) {
    if (ObjectUtil.isEmpty(map)) {
      return Maps.newHashMap();
    }
    Map<K, V> result = Maps.newLinkedHashMap();
    map.entrySet().stream().sorted(Map.Entry.<K, V>comparingByKey().reversed())
      .forEachOrdered(e -> result.put(e.getKey(), e.getValue()));

    return result;
  }

  public static <T> Collector<T, ?, List<T>> toSortedList(final Comparator<? super T> c) {
    return Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(c)), ArrayList::new);
  }

  public static <IN, OUT> List<OUT> map(final List<IN> list, final Function<IN, OUT> function) {
    if (ObjectUtil.isEmpty(list)) {
      return Lists.newArrayList();
    }
    return list.stream().map(function).collect(Collectors.toList());
  }

  public static <IN, OUT> Set<OUT> mapSet(final List<IN> list, final Function<IN, OUT> function) {
    if (ObjectUtil.isEmpty(list)) {
      return Sets.newHashSet();
    }
    return list.stream().map(function).collect(Collectors.toSet());
  }

  public static <IN, OUT> List<OUT> mapDict(final List<IN> list, final Function<IN, OUT> function) {
    if (ObjectUtil.isEmpty(list)) {
      return Lists.newArrayList();
    }
    return list.stream().map(function).distinct().collect(Collectors.toList());
  }

  public static <IN, OUT> List<OUT> mapFilter(final List<IN> list, final Function<IN, OUT> function,
                                              final Predicate<IN> predicate) {
    if (ObjectUtil.isEmpty(list)) {
      return Lists.newArrayList();
    }
    return list.stream().filter(predicate).map(function).collect(Collectors.toList());
  }

  public static <IN> List<IN> flatMapToList(final Collection<List<IN>> list) {
    if (ObjectUtil.isEmpty(list)) {
      return Lists.newArrayList();
    }
    return list.stream().flatMap(List::stream).collect(Collectors.toList());
  }

  public static <K, V, T> Map<K, V> toMap(final List<T> list, final Function<T, K> key, final Function<T, V> value) {
    if (ObjectUtil.isEmpty(list)) {
      return Maps.newHashMap();
    }
    return list.stream().collect(Collectors.toMap(key, value));
  }

  @SafeVarargs
  public static <K, V, T> Map<K, V> toMapFilter(final List<T> list,
                                                final Function<T, K> key,
                                                final Function<T, V> value,
                                                final Predicate<T>... predicates) {
    if (ObjectUtil.isEmpty(list)) {
      return Maps.newHashMap();
    }
    Stream<T> stream = list.stream();
    for (Predicate<T> predicate : predicates) {
      stream = stream.filter(predicate);
    }
    return stream.collect(Collectors.toMap(key, value, (v, v2) -> v2));
  }

  public static <IN, OUT> Map<OUT, List<IN>> groupBy(final List<IN> list, final Function<IN, OUT> function) {
    if (ObjectUtil.isEmpty(list)) {
      return Maps.newHashMap();
    }
    return list.stream().collect(Collectors.groupingBy(function));
  }

  public static <IN, OUT, VALUE> Map<OUT, List<VALUE>> groupBy(final List<IN> list, final Function<IN, OUT> function,
                                                               final Function<IN, VALUE> function2) {
    if (ObjectUtil.isEmpty(list)) {
      return Maps.newHashMap();
    }
    return list.stream().collect(Collectors.groupingBy(function, Collectors.mapping(function2, Collectors.toList())));
  }

  public static <E> List<List<E>> limit(final List<E> list, final Integer size) {
    // 继承了List，通过重写对应的方法来实现分隔
    return Lists.partition(list, size);
  }
}
